package com.zh.harmony.viewmodel;

import ohos.aafwk.ability.AbilityPackage;

import java.lang.reflect.InvocationTargetException;

/**
 * An utility class that provides {@code ViewModels} for a scope.
 * <p>
 * Default {@code ViewModelProvider} for an {@code Activity} or a {@code Fragment} can be obtained
 * by passing it to {@link ViewModelProvider#ViewModelProvider(ViewModelStoreOwner)}.
 */
@SuppressWarnings("WeakerAccess")
public class ViewModelProvider {
    /**
     * 缓存Key的前缀
     */
    private static final String DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey";

    /**
     * ViewModel工厂接口
     */
    public interface Factory {
        /**
         * 创建指定Class的ViewModel
         */
        <T extends ViewModel> T create(Class<T> modelClass);
    }

    static class OnRequeryFactory {
        void onRequery(ViewModel viewModel) {
        }
    }

    /**
     * 支持传入指定Key和Class，创建ViewModel
     */
    abstract static class KeyedFactory extends OnRequeryFactory implements Factory {
        /**
         * 可以指定Key和Class创建的create方法，旧的create(modelClass)将不再支持
         */
        public abstract <T extends ViewModel> T create(String key,
                                                       Class<T> modelClass);

        @Override
        public <T extends ViewModel> T create(Class<T> modelClass) {
            throw new UnsupportedOperationException("create(String, Class<?>) must be called on "
                    + "implementaions of KeyedFactory");
        }
    }

    private final Factory mFactory;
    private final ViewModelStore mViewModelStore;

    /**
     * 构造方法
     *
     * @param owner ViewModelStore持有者，如果它实现了HasDefaultViewModelProviderFactory接口，则通过它来获取默认工厂
     */
    public ViewModelProvider(ViewModelStoreOwner owner) {
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
    }

    /**
     * 构造方法，传入ViewModelStore和ViewModel工厂Factory
     */
    public ViewModelProvider(ViewModelStoreOwner owner, Factory factory) {
        this(owner.getViewModelStore(), factory);
    }

    /**
     * 构造方法，传入ViewModelStore和ViewModel工厂Factory
     */
    public ViewModelProvider(ViewModelStore store, Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }

    /**
     * 获取指定的ViewModel
     */
    public <T extends ViewModel> T get(Class<T> modelClass) {
        String canonicalName = modelClass.getCanonicalName();
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        //默认缓存Key是前缀 + ClassName
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }

    /**
     * 获取指定的ViewModel
     *
     * @param key        缓存Key
     * @param modelClass ViewModel的Class
     */
    @SuppressWarnings("unchecked")
    public <T extends ViewModel> T get(String key, Class<T> modelClass) {
        //先从缓存中获取
        ViewModel viewModel = mViewModelStore.get(key);
        //检查取出的实例是否和Class匹配
        if (modelClass.isInstance(viewModel)) {
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            //获取到直接返回
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        //没有获取到实例，则通过工厂创建
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        //保存实例到缓存中
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

    /**
     * 简单工厂，会调用无参构造实例化ViewModel
     */
    public static class NewInstanceFactory implements Factory {
        /**
         * 单例
         */
        private static NewInstanceFactory sInstance;

        /**
         * 获取工厂实例
         */
        static NewInstanceFactory getInstance() {
            if (sInstance == null) {
                sInstance = new NewInstanceFactory();
            }
            return sInstance;
        }

        @SuppressWarnings("ClassNewInstance")
        @Override
        public <T extends ViewModel> T create(Class<T> modelClass) {
            //noinspection TryWithIdenticalCatches
            try {
                //通过反射调用无参构造进行创建
                return modelClass.newInstance();
            } catch (InstantiationException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            }
        }
    }

    /**
     * HarmonyViewModel的工厂
     */
    public static class HarmonyViewModelFactory extends ViewModelProvider.NewInstanceFactory {
        /**
         * 单例
         */
        private static HarmonyViewModelFactory sInstance;

        /**
         * 获取工厂实例
         */
        public static HarmonyViewModelFactory getInstance(AbilityPackage application) {
            if (sInstance == null) {
                sInstance = new HarmonyViewModelFactory(application);
            }
            return sInstance;
        }

        private final AbilityPackage mApplication;

        public HarmonyViewModelFactory(AbilityPackage application) {
            mApplication = application;
        }

        @Override
        public <T extends ViewModel> T create(Class<T> modelClass) {
            //使用前，先判断传进来的Class，是否是HarmonyViewModel或其子类
            if (HarmonyViewModel.class.isAssignableFrom(modelClass)) {
                //noinspection TryWithIdenticalCatches
                try {
                    //调用只有一次参数，并且参数类型时AbilityPackage的构造方法
                    return modelClass.getConstructor(AbilityPackage.class).newInstance(mApplication);
                } catch (NoSuchMethodException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (InstantiationException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                }
            }
            return super.create(modelClass);
        }
    }
}